package com.szlanyou.thirdsdk.retrofit.client;

import android.content.Context;
import android.content.Intent;
import android.net.Uri;
import android.util.Log;

import com.szlanyou.thirdsdk.retrofit.callback.FileUploadProgressHttpResultHandlerHolder;
import com.szlanyou.thirdsdk.retrofit.callback.IFileUploadProgressHttpResultHandler;
import com.szlanyou.thirdsdk.retrofit.callback.IHttpResultHandler;
import com.szlanyou.thirdsdk.retrofit.exception.NoNetWorkException;
import com.szlanyou.thirdsdk.retrofit.file.download.FileDownloadObservableGenerator;
import com.szlanyou.thirdsdk.retrofit.file.download.IHttpDownloadProgressResultHandler;
import com.szlanyou.thirdsdk.retrofit.file.upload.FileUploadObservableGenerator;
import com.szlanyou.thirdsdk.retrofit.file.upload.UploadFileWrapper;
import com.szlanyou.thirdsdk.retrofit.interceptor.ShareInfoHelper;
import com.szlanyou.thirdsdk.retrofit.loading.NetLoadingStateAction;
import com.szlanyou.thirdsdk.retrofit.util.MyLogUtil;
import com.szlanyou.thirdsdk.retrofit.util.NetWorkUtil;

import org.jetbrains.annotations.NotNull;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.lang.ref.WeakReference;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;

import io.reactivex.Observable;
import io.reactivex.Observer;
import io.reactivex.android.schedulers.AndroidSchedulers;
import io.reactivex.disposables.Disposable;
import io.reactivex.functions.Function;
import io.reactivex.schedulers.Schedulers;
import okhttp3.ResponseBody;

/**
 * @author 陈章
 * create at 2020/11/19 14:55
 * desc:网络请求，与业务无关。
 * 处理Observable
 */
public class HttpMaster implements INetRequest {
    private static final String TAG = "HttpMaster";
    private WeakReference<Context> context;
    /**
     * 存储网络请求的句柄，用于取消网络请求。
     */
    private Map<String, Disposable> disposableMap = new HashMap<>();
    private Map<String, IHttpResultHandler> handlerMap = new HashMap<>();


    private NetLoadingStateAction loadingAction;
    private OnRequestInterceptor onRequestInterceptor;
    private HttpClient httpClient;
    private final RequestPageFlagManager requestPageFlagManager;

    public HttpMaster(Context context, HttpClient httpClient) {
        this.context = new WeakReference<>(context);
        this.httpClient = httpClient;
        this.requestPageFlagManager = new RequestPageFlagManager();
    }


    public void downloadFile(final boolean autoShowLoading, boolean autoDismissLoading, String loadingTipTxt, final String downloadUrl, final File outputDirOrFile, final IHttpDownloadProgressResultHandler httpDownloadProgressResultHandler, String requestPageFlag, String... requestCodeOut) {
        ShareInfoHelper.addDownloadUrl(downloadUrl);
        Observable<ResponseBody> observable = FileDownloadObservableGenerator.create(httpClient, downloadUrl);
        this.request(true, observable, autoShowLoading, autoDismissLoading, loadingTipTxt, new IHttpResultHandler<ResponseBody>() {
            @Override
            public void onStart(String requestPageFlag, String requestCode) {
                if (httpDownloadProgressResultHandler != null) {
                    httpDownloadProgressResultHandler.onStart(requestPageFlag, requestCode);
                }
            }

            @Override
            public void onSuccess(final String requestPageFlag, String requestCode, final ResponseBody responseBody) {
                final boolean[] errorHappen = {false};

                Observable.just(responseBody)
                        .subscribeOn(Schedulers.io())
                        .map(new Function<ResponseBody, File>() {
                            @Override
                            public File apply(ResponseBody responseBody) throws Exception {
                                return downloadFile(responseBody);
                            }
                        })
                        .observeOn(AndroidSchedulers.mainThread())
                        .subscribe(new Observer<File>() {
                            @Override
                            public void onSubscribe(Disposable d) {

                            }

                            @Override
                            public void onNext(File value) {
                                if (httpDownloadProgressResultHandler != null) {
                                    httpDownloadProgressResultHandler.onSuccess(requestPageFlag, requestCode, value);
                                }
                            }

                            @Override
                            public void onError(Throwable e) {
                                if (httpDownloadProgressResultHandler != null) {
                                    httpDownloadProgressResultHandler.onFailure(requestPageFlag, requestCode, null, e.getMessage());
                                }
                                errorHappen[0] = true;
                                onComplete();
                            }

                            @Override
                            public void onComplete() {
                                generalObserver.reset();

                                ShareInfoHelper.removeDownloadUrl(downloadUrl);
                                if (httpDownloadProgressResultHandler != null) {
                                    httpDownloadProgressResultHandler.onComplete(errorHappen[0]);
                                }
                            }
                        });
            }

            /**
             * 下载文件
             * @param responseBody
             */
            private File downloadFile(ResponseBody responseBody) throws IOException {
                String destFileDir = null;
                String destFileName = null;
                if (outputDirOrFile == null) {
                    destFileDir = context.get().getExternalFilesDir(null).getAbsolutePath();
                    destFileName = new File(downloadUrl).getName();
                } else if (outputDirOrFile.isFile() || outputDirOrFile.getName().contains(".") || !outputDirOrFile.getName().endsWith("/")) {
                    destFileDir = outputDirOrFile.getParent();
                    destFileName = outputDirOrFile.getName();
                } else {
                    destFileDir = outputDirOrFile.getAbsolutePath();
                    destFileName = new File(downloadUrl).getName();
                }

                {
                    InputStream is = null;
                    byte[] buf = new byte[1024];
                    int len = 0;
                    FileOutputStream fos = null;

                    //储存下载文件的目录
                    File dir = new File(destFileDir);
                    if (!dir.exists()) {
                        dir.mkdirs();
                    }
                    File file = new File(dir, destFileName);
                    if (file.exists()) {
                        file.delete();
                        try {
                            file.createNewFile();
                        } catch (IOException e) {
                            throw e;
                        }
                    }
                    try {
                        long total = responseBody.contentLength();
                        is = responseBody.byteStream();
                        fos = new FileOutputStream(file);
                        long sum = 0;
                        while ((len = is.read(buf)) != -1) {
                            fos.write(buf, 0, len);
                            sum += len;
                            if (httpDownloadProgressResultHandler != null) {
                                httpDownloadProgressResultHandler.onProgress(sum, total);
                            }
                        }
                        fos.flush();
                        context.get().sendBroadcast(new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE, Uri.fromFile(file)));
                        return file;
                    } catch (Exception e) {
                        e.printStackTrace();
                        throw e;
                    } finally {
                        try {
                            if (is != null) {
                                is.close();
                            }
                            if (fos != null) {
                                fos.close();
                            }
                        } catch (IOException e) {
                            e.printStackTrace();
                        }
                    }
                }
            }

            @Override
            public void onError(final String requestPageFlag, String requestCode, Throwable e) {
                if (httpDownloadProgressResultHandler != null) {
                    httpDownloadProgressResultHandler.onError(requestPageFlag, requestCode, e);
                }
            }

            @Override
            public void onFailure(final String requestPageFlag, String requestCode, String responseCode, String msg) {
                if (httpDownloadProgressResultHandler != null) {
                    httpDownloadProgressResultHandler.onFailure(requestPageFlag, requestCode, responseCode, msg);
                }
            }

            @Override
            public void onCancel(final String requestPageFlag, String requestCode) {
                if (httpDownloadProgressResultHandler != null) {
                    httpDownloadProgressResultHandler.onCancel(requestPageFlag, requestCode);
                }
            }

            @Override
            public void onComplete(boolean causeByError) {
                //对于下载onComplete如果是onSuccess触发的，不能回调。因为onSuccess会执行异步的文件下载，此时文件一般并未下载完毕。
                if (httpDownloadProgressResultHandler != null && causeByError) {
                    ShareInfoHelper.removeDownloadUrl(downloadUrl);
                    httpDownloadProgressResultHandler.onComplete(causeByError);
                }
            }
        }, requestPageFlag, requestCodeOut);
    }

    /**
     * 非下载相关的请求 (带requestPageFlag)
     *
     * @param observable
     * @param autoShowLoading
     * @param loadingTipTxt
     * @param httpResultHandler
     * @param requestCodeOut
     * @param <T>
     * @param <K>
     */
    public <T, K> void request(Observable<T> observable, final boolean autoShowLoading, boolean autoDismissLoading, String loadingTipTxt, final IHttpResultHandler<K> httpResultHandler, String requestPageFlag, String... requestCodeOut) {
        this.request(false, observable, autoShowLoading, autoDismissLoading, loadingTipTxt, httpResultHandler, requestPageFlag, requestCodeOut);
    }

    /**
     * 通用请求 (带requestPageFlag)
     *
     * @param isDownloadFile
     * @param observable
     * @param autoShowLoading
     * @param loadingTipTxt
     * @param httpResultHandler
     * @param requestCodeOut
     * @param <T>
     * @param <K>
     */
    private <T, K> void request(boolean isDownloadFile, Observable<T> observable, final boolean autoShowLoading, boolean autoDismissLoading, String loadingTipTxt, final IHttpResultHandler<K> httpResultHandler, String requestPageFlag, String... requestCodeOut) {
        String requestCode = null;
        if (requestCodeOut != null && requestCodeOut.length > 0) {
            requestCode = requestCodeOut[0];
        }

        final String finalRequestCode;
        if (requestCode == null) {
            if (requestPageFlag == null) {
                finalRequestCode = String.valueOf(disposableMap.size());
            } else {
                finalRequestCode = requestPageFlagManager.generateRequestCode(requestPageFlag);
            }
        } else {
            //指定了requestCode，则requestCode优先级高于requestPageFlag
            finalRequestCode = requestCode;
        }

        if (httpResultHandler != null) {
            httpResultHandler.onStart(requestPageFlag, requestCode);
        }

        if (onRequestInterceptor != null) {
            onRequestInterceptor.onRequestStartIntercept(requestCodeOut);
        }

        Observer subscriber;
        if (isDownloadFile) {
            subscriber = createFileSubscriber(autoShowLoading, autoDismissLoading, httpResultHandler, requestPageFlag, finalRequestCode);
        } else {
            subscriber = createGeneralSubscriber(autoShowLoading, autoDismissLoading, httpResultHandler, requestPageFlag, finalRequestCode);
        }
        if (!NetWorkUtil.isNetWorkEnable(context.get())) {
            NoNetWorkException noNetWorkException = new NoNetWorkException();
            MyLogUtil.e(noNetWorkException.getMessage());
            subscriber.onError(noNetWorkException);
            return;
        }

        if (autoShowLoading) {
            loadingAction = httpClient.getLoadingAction();
            if (loadingAction != null) {
                MyLogUtil.d(TAG, "autoShowLoading ,loadingAction is " + loadingAction);
                if (loadingTipTxt == null) {
                    loadingAction.showLoading();
                } else {
                    loadingAction.showLoading(loadingTipTxt);
                }
            } else {
                MyLogUtil.e(TAG, "autoShowLoading but loadingAction is null");
            }
        }
        observable.subscribeOn(Schedulers.io())
                .observeOn(AndroidSchedulers.mainThread())
                .subscribe(subscriber);
    }

    /**
     * 通用的网络请求Observer监听
     *
     * @param <T>
     * @param <K>
     */
    protected class GeneralObserver<T, K> implements Observer<T>, IGeneralObserverReset {
        protected final boolean autoShowLoading;
        protected final boolean autoDismissLoading;
        protected final IHttpResultHandler<K> httpResultHandler;
        protected final String requestPageFlag;
        protected final String finalRequestCode;
        protected boolean errorHappened;

        public GeneralObserver(final boolean autoShowLoading, boolean autoDismissLoading, final IHttpResultHandler<K> httpResultHandler, final String requestPageFlag, final String finalRequestCode) {
            this.autoShowLoading = autoShowLoading;
            this.autoDismissLoading = autoDismissLoading;
            this.httpResultHandler = httpResultHandler;
            this.requestPageFlag = requestPageFlag;
            this.finalRequestCode = finalRequestCode;
            this.errorHappened = false;
            this.httpResultHandler.attachGeneralObserver(this);
        }

        @Override
        public void onSubscribe(Disposable d) {
            disposableMap.put(finalRequestCode, d);
            handlerMap.put(finalRequestCode, httpResultHandler);
        }

        @Override
        public void onNext(T value) {
            if (httpResultHandler != null) {
                httpResultHandler.onSuccess(requestPageFlag, finalRequestCode, (K) value);
            }
        }

        @Override
        public void onError(Throwable e) {
            if (autoShowLoading && loadingAction != null) {
                loadingAction.showFailure(e);
            }
            if (httpResultHandler != null) {
                httpResultHandler.onError(requestPageFlag, finalRequestCode, e);
            }
            this.errorHappened = true;
            onComplete();
        }

        @Override
        public void onComplete() {
            if (httpResultHandler != null) {
                //从缓存移除，处理完毕了，拦截器里就不需要回调上传进度。
                IHttpResultHandler originalHttpResultHandler = httpResultHandler.getOriginalHttpResultHandler();
                if (originalHttpResultHandler instanceof IFileUploadProgressHttpResultHandler) {
                    FileUploadProgressHttpResultHandlerHolder.remove(originalHttpResultHandler.getUniqueRequestId());
                }
                httpResultHandler.onComplete(errorHappened);
            }
            reset();
        }

        /**
         * 资源释放，关闭对话框。
         */
        @Override
        public void reset() {
            disposableMap.remove(finalRequestCode);
            handlerMap.remove(finalRequestCode);
            requestPageFlagManager.clearRequestCodeSet(requestPageFlag);

            if (loadingAction != null) {
                if (autoDismissLoading) {
                    loadingAction.hideLoading();
                }
            }
        }
    }

    @NotNull
    private <T, K> Observer<T> createGeneralSubscriber(final boolean autoShowLoading, boolean autoDismissLoading, final IHttpResultHandler<K> httpResultHandler, final String requestPageFlag, final String finalRequestCode) {
        return new GeneralObserver(autoShowLoading, autoDismissLoading, httpResultHandler, requestPageFlag, finalRequestCode);
    }

    /**
     * 文件下载
     *
     * @param <T>
     * @param <K>
     */
    protected class DownloadFileObserver<T, K> extends GeneralObserver<T, K> {
        public DownloadFileObserver(boolean autoShowLoading, boolean autoDismissLoading, IHttpResultHandler httpResultHandler, final String requestPageFlag, String finalRequestCode) {
            super(autoShowLoading, autoDismissLoading, httpResultHandler, requestPageFlag, finalRequestCode);
        }

        /**
         * 对于下载文件接口，onSuccess触发的onComplete只是ResponseBody请求成功了，不是文件下载成功了。
         * <p>
         * 这里复写父类方法，是为了不执行{@link #reset()}方法，否则下载请求的Disposable被移除了，下载就无法取消中断。
         */
        @Override
        public void onComplete() {
            if (httpResultHandler != null && errorHappened) {
                httpResultHandler.onComplete(true);
            }
        }
    }

    @NotNull
    private <T, K> Observer<T> createFileSubscriber(final boolean autoShowLoading, boolean autoDismissLoading, final IHttpResultHandler<K> httpResultHandler, final String requestPageFlag, final String finalRequestCode) {
        return new DownloadFileObserver(autoShowLoading, autoDismissLoading, httpResultHandler, requestPageFlag, finalRequestCode);
    }

    @Override
    public void dismissLoading() {
        if (loadingAction != null) {
            loadingAction.hideLoading();
        }
    }

    /**
     * 主动取消网络请求
     *
     * @param requestCodeOut
     */
    @Override
    public void cancel(String requestCodeOut) {
        cancel(requestCodeOut, disposableMap.get(requestCodeOut));
    }

    @Override
    public void cancelByPageFlag(String requestPageFlag) {
        Log.d(TAG, "cancelByPageFlag " + requestPageFlag);
        cancel(requestPageFlagManager.getRequestCodeSet(requestPageFlag));
        requestPageFlagManager.clearRequestCodeSet(requestPageFlag);
    }

    @Override
    public void cancel(Set<String> requestCodeSet) {
        Log.d(TAG, "cancel part");
        if (requestCodeSet == null) {
            Log.e(TAG, "requestCodeSet is null");
            return;
        }
        for (String requestCode : requestCodeSet) {
            cancel(requestCode);
        }
    }

    /**
     * 取消所有的网络请求
     */
    @Override
    public void cancelAll() {
        Log.d(TAG, "cancelAll");
        for (Iterator<Map.Entry<String, Disposable>> it = disposableMap.entrySet().iterator(); it.hasNext(); ) {
            Map.Entry<String, Disposable> item = it.next();
            cancel(item.getKey(), item.getValue());
        }
    }

    private void cancel(String requestCode, Disposable disposable) {
        Log.d(TAG, "cancel prepare requestCode = " + requestCode);
        if (disposable != null) {
            if (!disposable.isDisposed()) {
                Log.d(TAG, "cancel start requestCode = " + requestCode);
                disposable.dispose();
                //这里要remove一下，因为如果cancel没有导致异常的话，不会走onComplete，会导致内存泄漏。
                disposableMap.remove(requestCode);

                IHttpResultHandler httpResultHandler = handlerMap.get(requestCode);
                if (httpResultHandler != null) {
                    httpResultHandler.onCancel(null, requestCode);
                    handlerMap.remove(requestCode);
                }
            }
        }
    }

    @Override
    public void setOnRequestInterceptor(OnRequestInterceptor onRequestInterceptor) {
        this.onRequestInterceptor = onRequestInterceptor;
    }

    public interface OnRequestInterceptor {
        /**
         * 统一的网络请求的拦截
         */
        void onRequestStartIntercept(String... requestCodeOut);
    }

    public HttpClient getHttpClient() {
        return httpClient;
    }
}
